Skip to content

Conversation

@QMalcolm
Copy link
Contributor

resolves #1345

Problem

dbt-core added the ability to set function node volatility in dbt-labs/dbt-core#12100. Thus in adapters we needed to begin interpolating the correct volatility value into the function materialization macros.

Solution

  • Create new macros for interpolating volatility for a given function node
  • Test that the volatility gets set (and still successfully creates the function)

Checklist

  • I have read the contributing guide and understand what's expected of me
  • I have run this code in development and it appears to resolve the stated issue
  • This PR includes tests, or tests are not required/relevant for this PR
  • This PR has no interface changes (e.g. macros, cli, logs, json artifacts, config files, adapter interface, etc) or this PR has already received feedback and approval from Product or DX

Comment on lines 17 to 24
{% if model.config.get('volatility') == 'deterministic' %}
IMMUTABLE
{% elif model.config.get('volatility') == 'stable' %}
STABLE
{% else %}
{# At this point, either they've set `non-deterministic` or they've set nothing. In either case, we default to VOLATILE #}
VOLATILE
{% endif %}
Copy link
Contributor

@colin-rogers-dbt colin-rogers-dbt Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
{% if model.config.get('volatility') == 'deterministic' %}
IMMUTABLE
{% elif model.config.get('volatility') == 'stable' %}
STABLE
{% else %}
{# At this point, either they've set `non-deterministic` or they've set nothing. In either case, we default to VOLATILE #}
VOLATILE
{% endif %}
{% set volatility = model.config.get('volatility') %}
{% if volatility == 'deterministic' %}
IMMUTABLE
{% elif volatility == 'stable' %}
STABLE
{% else %}
{# At this point, either they've set `non-deterministic` or they've set nothing. In either case, we default to VOLATILE #}
VOLATILE
{% endif %}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also do we need to do any validation/warning if they have set something other than these options?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(also in the future if we add another option do we need to remember to come back and raise a warning?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Core only accepts an Enum of possible volatility types deterministic, stable, and non-deterministic. So in order for a 4th option to come into existence, we'd first have to extend the enum in core. Additionally, have a hard time imagining any other volatility type being invented, but perhaps I'm being unimaginative 🤷🏻 Future proofing doesn't hurt anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I think I've now added logic to sufficiently handle this 🙂

Comment on lines 16 to 22
{% if model.config.get('volatility') == 'deterministic' %}
IMMUTABLE
{% elif model.config.get('volatility') == 'stable' %}
{% do exceptions.warn("`Stable` function volatility is not supported by Snowflake, and will be ignored") %}
{% elif model.config.get('volatility') == 'non-deterministic' %}
VOLATILE
{% endif %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this more future proof?

Suggested change
{% if model.config.get('volatility') == 'deterministic' %}
IMMUTABLE
{% elif model.config.get('volatility') == 'stable' %}
{% do exceptions.warn("`Stable` function volatility is not supported by Snowflake, and will be ignored") %}
{% elif model.config.get('volatility') == 'non-deterministic' %}
VOLATILE
{% endif %}
{% set volatility = model.config.get('volatility') %}
{% if volatility == 'deterministic' %}
IMMUTABLE
{% elif model.config.get('volatility') == 'non-deterministic' %}
VOLATILE
{% elif volatility is not none %}
{% do exceptions.warn("user passed unsupported volatility config value: " ~ volatility ~"; it will be ignored") %}
{% endif %}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I think I've now added logic to sufficiently handle this 🙂

"Programming Language :: Python :: 3.12",
]
dependencies = [
"dbt-common>=1.34.0,<2.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we let dbt-adapters set the range for dbt-common here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can. My thought here was that we're specifying this range specifically for testing purposes (the EventCatcher is only used in testing). If putting it in dbt-adapters is more straight forward though, I'm good with doing so.

Technically it should be impossible for a new volatility to be created
without our knowledge of it and for it to reach dbt-adapters macros. That
is because `volatility` is defined as an `Enum` in core with only three
possible values `deterministic`, `stable`, and `non-deterministic`. But
if a new volatility is invented, and we start supporting setting it in core
then we will want to handle it in dbt-adapters (by default as a warning) until
we add _specific_ handling in dbt-adapters to support it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla:yes The PR author has signed the CLA

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[UDFs] A function's volatility is propagated to the create/replace function logic

3 participants